package cn.elvea.security;

import cn.elvea.domain.Permission;
import cn.elvea.domain.User;
import cn.elvea.service.UserService;
import cn.elvea.utils.EncryptUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import java.util.Set;
import java.util.stream.Collectors;

public class ShiroSecurityRealm extends AuthorizingRealm {
    private static final Logger logger = LoggerFactory.getLogger(ShiroSecurityRealm.class);

    @Autowired
    UserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        logger.debug("doGetAuthorizationInfo...");
        SimpleAuthorizationInfo info = null;

        ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
        User user = userService.findByUsername(shiroUser.username);
        if (user != null) {
            logger.debug("user [" + shiroUser.username + "] found.");
            info = new SimpleAuthorizationInfo();
            info.addRole(user.getRole().getCode());
            if (!user.getPermissions().isEmpty()) {
                Set<String> ps = user.getPermissions().stream().map(Permission::getName).collect(Collectors.toSet());
                info.setStringPermissions(ps);
            }
        } else {
            logger.debug("user [" + shiroUser.username + "] not found.");
        }
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        logger.debug("doGetAuthenticationInfo...");
        logger.debug(" admin - admin " + EncryptUtils.encryptPassword("admin", "admin"));
        logger.debug(" sysadmin - sysadmin " + EncryptUtils.encryptPassword("sysadmin", "sysadmin"));
        logger.debug(" test - test " + EncryptUtils.encryptPassword("test", "test"));

        SimpleAuthenticationInfo info = null;

        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;

        User user = userService.findByUsername(token.getUsername());
        if (user != null) {
            logger.debug("user [" + token.getUsername() + "] found.");

            info = new SimpleAuthenticationInfo(new ShiroUser(user.getUsername(), user.getUsername()), user.getPassword(), getName());
            info.setCredentialsSalt(ByteSource.Util.bytes(user.getSalt()));
        } else {
            logger.debug("user [" + token.getUsername() + "] not found.");
        }
        return info;
    }

    @PostConstruct
    public void initCredentialsMatcher() {
        setCredentialsMatcher(new ShiroCredentialsMatcher());
    }
}